#include "wifi_provisioning.h"
#include <cstdio>
#include <cstring>
#include <memory>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/event_groups.h>
#include <esp_err.h>
#include <esp_event.h>
#include <esp_wifi.h>
#include <esp_log.h>
#include <esp_mac.h>
#include <esp_netif.h>
#include <nvs.h>
#include <nvs_flash.h>
#include <esp_smartconfig.h>
#include "ssid_manager.h"
#include "wifi_provisioning/manager.h"
#include "wifi_provisioning/scheme_ble.h"

#define TAG "WifiConfigurationBLE"

#define WIFI_CONNECTED_EVENT BIT0
#define WIFI_FAIL_BIT      BIT1

//static EventGroupHandle_t wifi_event_group;

#define DEVICE_SN   "v20250701-01"
#define PROV_QR_VERSION "v1"
#define PROV_TRANSPORT_BLE "ble"
#define CONFIG_EXAMPLE_PROV_MGR_MAX_RETRY_CNT 3    // 最大重试次数

uint8_t wifi_config_suc_flag = 0;

WifiPovisioningBLE& WifiPovisioningBLE::GetInstance() {
    static WifiPovisioningBLE instance;
    return instance;
}

WifiPovisioningBLE::WifiPovisioningBLE()
{
    event_group_ = xEventGroupCreate();
    language_ = "zh-CN";
}

WifiPovisioningBLE::~WifiPovisioningBLE()
{
    if (scan_timer_) {
        esp_timer_stop(scan_timer_);
        esp_timer_delete(scan_timer_);
    }
    if (event_group_) {
        vEventGroupDelete(event_group_);
    }
    // Unregister event handlers if they were registered
    if (instance_any_id_) {
        esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id_);
    }
    if (instance_got_ip_) {
        esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip_);
    }
}

void WifiPovisioningBLE::SetLanguage(const std::string &&language)
{
    language_ = language;
}

void WifiPovisioningBLE::SetSsidPrefix(const std::string &&ssid_prefix)
{
    ssid_prefix_ = ssid_prefix;
}

void WifiPovisioningBLE::Start()
{
    // Register event handlers
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_PROV_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &WifiPovisioningBLE::WifiEventHandler,
                                                        this,
                                                        &instance_any_id_));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &WifiPovisioningBLE::WifiEventHandler,
                                                        this,
                                                        &instance_any_id_));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &WifiPovisioningBLE::WifiEventHandler,
                                                        this,
                                                        &instance_got_ip_));
     ESP_ERROR_CHECK(esp_event_handler_instance_register(PROTOCOMM_TRANSPORT_BLE_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &WifiPovisioningBLE::WifiEventHandler,
                                                        this,
                                                        &instance_any_id_));                                                                                                               
    StartBLEProvisioning();
    // Start scan immediately
    esp_wifi_scan_start(nullptr, false);
    // Setup periodic WiFi scan timer
    esp_timer_create_args_t timer_args = {
        .callback = [](void* arg) {
            auto* self = static_cast<WifiPovisioningBLE*>(arg);
            if (!self->is_connecting_) {
                esp_wifi_scan_start(nullptr, false);
            }
        },
        .arg = this,
        .dispatch_method = ESP_TIMER_TASK,
        .name = "wifi_scan_timer",
        .skip_unhandled_events = true
    };
    ESP_ERROR_CHECK(esp_timer_create(&timer_args, &scan_timer_));
}

//获取设备名称
std::string WifiPovisioningBLE::GetDeviceName()
{
    // Get MAC and use it to generate a unique SSID
    uint8_t mac[6];
#if CONFIG_IDF_TARGET_ESP32P4
    esp_wifi_get_mac(WIFI_IF_STA, mac);
#else
    ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_STA));
#endif
    char ssid[32] = {0};
    snprintf(ssid, sizeof(ssid), "%s-%02X%02X%02X%02X%02X%02X", ssid_prefix_.c_str(),mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return std::string(ssid);
}

void WifiPovisioningBLE::StartBLEProvisioning()
{
    // 网络初始化
    ESP_ERROR_CHECK(esp_netif_init());

    // 初始化Wi-Fi
    esp_netif_create_default_wifi_sta();
    wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&wifi_cfg));

    // 蓝牙配网管理器配置
    wifi_prov_mgr_config_t prov_cfg = {
        .scheme = wifi_prov_scheme_ble,
        .scheme_event_handler = WIFI_PROV_SCHEME_BLE_EVENT_HANDLER_FREE_BTDM
    };
    ESP_ERROR_CHECK(wifi_prov_mgr_init(prov_cfg));
    ESP_LOGI(TAG, "启动蓝牙配网流程");
    
    char service_name[32] = {0};
    GetDeviceName().copy(service_name, 32);
    
    // 启动蓝牙配网
    ESP_ERROR_CHECK(wifi_prov_mgr_start_provisioning(
        WIFI_PROV_SECURITY_1,      // 使用简单安全模式
        "abcd1234",                // 配对密码
        service_name, 
        NULL));     

    // 等待配网完成
    xEventGroupWaitBits(event_group_, WIFI_CONNECTED_EVENT | WIFI_FAIL_BIT, pdTRUE, pdFALSE, pdMS_TO_TICKS(10000)); 

}


void WifiPovisioningBLE::Save(const std::string &ssid, const std::string &password)
{
    ESP_LOGI(TAG, "Save SSID %s %d", ssid.c_str(), ssid.length());
    SsidManager::GetInstance().AddSsid(ssid, password);
}

void WifiPovisioningBLE::WifiEventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    WifiPovisioningBLE* self = static_cast<WifiPovisioningBLE*>(arg);

    static int retries;
    if (event_base == WIFI_PROV_EVENT) {
        switch (event_id) {
            case WIFI_PROV_START:
                ESP_LOGI(TAG, "配网开始");
                break;
            case WIFI_PROV_CRED_RECV: {
                wifi_sta_config_t *wifi_sta_cfg = (wifi_sta_config_t *)event_data;
                ESP_LOGI(TAG, "收到Wi-Fi凭证" "\n\tSSID : %s\n\t密码 : %s",
                         (const char *) wifi_sta_cfg->ssid, (const char *) wifi_sta_cfg->password);
                // 暂存Wi-Fi凭证
                self->ssid_ = std::string((const char *)wifi_sta_cfg->ssid);
                self->password_ = std::string((const char *)wifi_sta_cfg->password);
                break;
            }
            case WIFI_PROV_CRED_FAIL: {
                wifi_prov_sta_fail_reason_t *reason = (wifi_prov_sta_fail_reason_t *)event_data;
                ESP_LOGE(TAG, "配网失败!\n\t原因 : %s" "\n\t请重置到出厂设置并重试配网",
                         (*reason == WIFI_PROV_STA_AUTH_ERROR) ? "Wi-Fi站点认证失败" : "未找到Wi-Fi接入点");
                retries++;
                if (retries >= CONFIG_EXAMPLE_PROV_MGR_MAX_RETRY_CNT) {
                    ESP_LOGI(TAG, "无法连接到已配网的AP，正在重置配网凭证");
                    wifi_prov_mgr_reset_sm_state_on_failure();
                    retries = 0;
                }
                self->is_connecting_ = false;
                break;
            }
            case WIFI_PROV_CRED_SUCCESS:
                ESP_LOGI(TAG, "配网成功");
                // 保存Wi-Fi凭证
                if (!self->ssid_.empty() && !self->password_.empty()) {
                    self->Save(self->ssid_, self->password_);
                    ESP_LOGI(TAG, "已保存Wi-Fi凭证");
                }
                self->is_connecting_ = false;
                break;
                retries = 0;
                break;
            case WIFI_PROV_END:
                /* 配网完成后反初始化管理器 */
                ESP_LOGW(TAG,"配网成功结束，正在解除配网管理器...\n");
                wifi_prov_mgr_deinit();
                // 设备重启标志位
                wifi_config_suc_flag = 1;
                //设备重启
                ESP_LOGW(TAG,"即将软重启设备...\n");      
                break;
            default:
                break;
        }
    } else if (event_base == WIFI_EVENT) {
        switch (event_id) {
            case WIFI_EVENT_STA_START:
                ESP_LOGI(TAG, "启动WiFi连接...");
                // 延迟100ms后启动Wi-Fi连接
                self->is_connecting_ = true;
                esp_wifi_scan_stop();
                vTaskDelay(100 / portTICK_PERIOD_MS);
                esp_wifi_connect();
                break;
            case WIFI_EVENT_STA_DISCONNECTED:
                ESP_LOGI(TAG, "已断开。正在重新连接到AP...");
                self->is_connecting_ = true;
                esp_wifi_connect();
                break;
            default:
                break;
        }
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "已连接，IP地址:" IPSTR, IP2STR(&event->ip_info.ip));
        /* 通知主应用继续执行 */
        xEventGroupSetBits(self->event_group_, WIFI_CONNECTED_EVENT);

    } else if (event_base == PROTOCOMM_TRANSPORT_BLE_EVENT) {
        switch (event_id) {
            case PROTOCOMM_TRANSPORT_BLE_CONNECTED:
                ESP_LOGI(TAG, "BLE传输：已连接！");
                break;
            case PROTOCOMM_TRANSPORT_BLE_DISCONNECTED:
                ESP_LOGI(TAG, "BLE传输：已断开！");
                break;
            default:
                break;
        }
    } else if (event_base == PROTOCOMM_SECURITY_SESSION_EVENT) {
        switch (event_id) {
            case PROTOCOMM_SECURITY_SESSION_SETUP_OK:
                ESP_LOGI(TAG, "安全会话已建立！");
                break;
            case PROTOCOMM_SECURITY_SESSION_INVALID_SECURITY_PARAMS:
                ESP_LOGE(TAG, "收到无效的安全参数以建立安全会话！");
                break;
            case PROTOCOMM_SECURITY_SESSION_CREDENTIALS_MISMATCH:
                ESP_LOGE(TAG, "收到不正确的用户名和/或PoP以建立安全会话！");
                break;
            default:
                break;
        }
    }
}


void WifiPovisioningBLE::StartSmartConfig()
{
    // 注册SmartConfig事件处理器
    ESP_ERROR_CHECK(esp_event_handler_instance_register(SC_EVENT, ESP_EVENT_ANY_ID,
                                                        &WifiPovisioningBLE::SmartConfigEventHandler, this, &sc_event_instance_));

    // 初始化SmartConfig配置
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    // cfg.esp_touch_v2_enable_crypt = true;
    // cfg.esp_touch_v2_key = "1234567890123456"; // 设置16字节加密密钥

    // 启动SmartConfig服务
    ESP_ERROR_CHECK(esp_smartconfig_start(&cfg));
    ESP_LOGI(TAG, "SmartConfig started");
}

void WifiPovisioningBLE::SmartConfigEventHandler(void *arg, esp_event_base_t event_base,
                                                  int32_t event_id, void *event_data)
{
    WifiPovisioningBLE *self = static_cast<WifiPovisioningBLE *>(arg);

    if (event_base == SC_EVENT){
        switch (event_id){
        case SC_EVENT_SCAN_DONE:
            ESP_LOGI(TAG, "SmartConfig scan done");
            break;
        case SC_EVENT_FOUND_CHANNEL:
            ESP_LOGI(TAG, "Found SmartConfig channel");
            break;
        case SC_EVENT_GOT_SSID_PSWD:{
            ESP_LOGI(TAG, "Got SmartConfig credentials");
            smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;

            char ssid[32], password[64];
            memcpy(ssid, evt->ssid, sizeof(evt->ssid));
            memcpy(password, evt->password, sizeof(evt->password));
            ESP_LOGI(TAG, "SmartConfig SSID: %s, Password: %s", ssid, password);
            // 尝试连接WiFi会失败，故不连接
            self->Save(ssid, password);
            xTaskCreate([](void *ctx){
                ESP_LOGI(TAG, "Restarting in 3 second");
                vTaskDelay(pdMS_TO_TICKS(3000));
                esp_restart();
            }, "restart_task", 4096, NULL, 5, NULL);
            break;
        }
        case SC_EVENT_SEND_ACK_DONE:
            ESP_LOGI(TAG, "SmartConfig ACK sent");
            esp_smartconfig_stop();
            break;
        }
    }
}

void WifiPovisioningBLE::Stop() {
    // 停止SmartConfig服务
    if (sc_event_instance_) {
        esp_event_handler_instance_unregister(SC_EVENT, ESP_EVENT_ANY_ID, sc_event_instance_);
        sc_event_instance_ = nullptr;
    }
    esp_smartconfig_stop();

    // 停止定时器
    if (scan_timer_) {
        esp_timer_stop(scan_timer_);
        esp_timer_delete(scan_timer_);
        scan_timer_ = nullptr;
    }

    // 注销事件处理器
    if (instance_any_id_) {
        esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id_);
        instance_any_id_ = nullptr;
    }
    if (instance_got_ip_) {
        esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip_);
        instance_got_ip_ = nullptr;
    }

    // 停止WiFi并重置模式
    esp_wifi_stop();
    esp_wifi_deinit();
    esp_wifi_set_mode(WIFI_MODE_NULL);

    ESP_LOGI(TAG, "Wifi provisioning by BLE stopped");
}
